package com.googlecode.pacswitch;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;

/**
 * @author Andrey Pavlenko
 */
@XmlType(name = "bind", namespace = ObjectFactory.NS)
public class Bind {
    private PString _host;
    private PInteger _port;
    private AsynchronousServerSocketChannel _serverSocket;

    @XmlAttribute
    public PString getHost() {
        return (_host == null) ? new PString("localhost") : _host;
    }

    public void setHost(final PString host) {
        _host = host;
    }

    @XmlAttribute
    public PInteger getPort() {
        return _port;
    }

    public void setPort(final PInteger port) {
        _port = port;
    }

    synchronized boolean isBound() {
        return _serverSocket != null;
    }

    synchronized <H extends CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel>> void bind(
            final PacSwitch ps, final H handler,
            final AsynchronousChannelGroup channelGroup) throws IOException {
        final Log log = ps.getConfig().getLog();
        final String host = getHost().resolve(ps, null);
        final int port = getPort().resolveInt(ps, null);
        final InetSocketAddress address = new InetSocketAddress(host, port);
        final AsynchronousServerSocketChannel ss = AsynchronousServerSocketChannel
                .open(channelGroup).bind(address);
        ss.accept(ss, handler);
        _serverSocket = ss;

        if (log.isInfoEnabled()) {
            log.info("Listening socket: " + address);
        }
    }

    synchronized void unbind(final PacSwitch ps) {
        if (isBound()) {
            final Log log = ps.getConfig().getLog();

            try {
                if (log.isInfoEnabled()) {
                    log.info("Closing socket: " + _serverSocket);
                }
                _serverSocket.close();
            } catch (final Throwable ex) {
                log.error("Error occurred", ex);
            } finally {
                _serverSocket = null;
            }
        }
    }
}
